08. Java Linker

Java Linker

ND079 JPND C3 L3 A07 Java Linker V3

JLink is a tool that allows us to create a custom Java Runtime Environment containing the minimal components necessary to run a specific Java module. This command line tool is part of the JDK, so you can run it by simply typing jlink from any command prompt that has the Java /bin directory on the path.

JLink only works on modules. We'll demonstrate its use on a simple example.

App.java

import java.math.BigInteger;
import java.util.Set;
public class App 
{
    public static void main( String[] args )
    {
        Set<Integer> newSet = Set.of(1,2,3,4);
        int unnecessarilyComplexSum = newSet.stream()
                .map(BigInteger::valueOf)
                .reduce(BigInteger.ZERO,(a, b) -> a.add(b)).intValue();
        System.out.println( "Hello " + unnecessarilyComplexSum );
    }
}

module-info.java

module com.udacity.jpnd.moduletest {
}

Analyze dependencies

The first step to using JLink is to make sure that all your dependencies can be resolved. The JDK includes a tool for this called jdeps. After building the above into a jar, we can run jdeps on the jar:

jdeps Example.jar

This shows us all the dependencies required by the program and where they are located.

   requires mandated java.base (@14.0.1)
com.udacity.jpnd.moduletest -> java.base
   com.udacity.jpnd                                   -> java.io                                            java.base
   com.udacity.jpnd                                   -> java.lang                                          java.base
   com.udacity.jpnd                                   -> java.lang.invoke                                   java.base
   com.udacity.jpnd                                   -> java.math                                          java.base
   com.udacity.jpnd                                   -> java.util                                          java.base
   com.udacity.jpnd                                   -> java.util.function                                 java.base
   com.udacity.jpnd                                   -> java.util.stream                                   java.base

Because all our dependencies are part of java.base we can go ahead and begin linking. If your program has additional dependencies, you can use jdeps to determine whether you need to manually include additional packages in your jar.

Building Runtime

To run JLink, we must put all the required modules on the modulepath. In this case, that includes the module for our program as well as java.base. Then we use the --add-modules flag to specify which module to add to our JRE, and lastly we provide an output directory. The modules comprising the Java Standard Library can be found in the /jmods directory of your Java install.

jlink --module-path "$JAVA_HOME/jmods" --module-path target/classes --add-modules com.udacity.jpnd.moduletest --output tinyJRE

Using the Runtime

The new tinyJRE directory is less than 40 MB and contains a complete Java Runtime that can run our program.

tinyJRE/bin/java.exe -jar Example.jar

Resolving dependencies for projects that include non-modular dependencies can be challenging and is not covered by this introduction, but there are some Maven plugins that can assist, such as the Maven JDeps Plugin and the Moditect plugin.

Additional Resources

Building minimal JREs from larger projects can be complex. Here are some additional resources if you need to learn more about this topic.

https://docs.oracle.com/en/java/javase/15/docs/specs/man/jdeps.html

https://docs.oracle.com/en/java/javase/15/docs/specs/man/jlink.html

https://maven.apache.org/plugins/maven-jdeps-plugin/

https://github.com/moditect/moditect